
var CORNER_COUNT = 0;
var CORNER_FLAG = 0;
// @author U099151W Sun Chenchen
// all functions in this file  are implemented by the author unless specifically indicated
var AI = function() {
    var STUDENT_ATTACK_RANGE_X = 1;
    var STUDENT_ATTACK_RANGE_Y = 0;
    var ESCAPE_THRESHOLD = 7;
    var REACTION_AGGRESSIVE = 5;
    var REACTION_NORMAL = 5;
    var REACTION_PASSIVE = 5;

    var _scene;
    var _hitTimer = 1;
    var _NPCmove = 0;
    var _inRange = 0;

    var _waveIndex = 0;
    this.getWaveIndex = function() { return _waveIndex; };
    var _waveIndexTmp = 0;
    var _waveList = [];

    var _specialGridList = new Array(8);
    var _specialGridIndex = 0;

    var _inHollyWoodScenario = false;
    var _initHollyWoodScenario = false;
    var _hollyWoodScenarioIndex = 1;
    this.clearInHolly = function() {
        _inHollyWoodScenario = false;
        _initHollyWoodScenario = false;
    }
    this.setInHolly = function() {
        _inHollyWoodScenario = true;
        _initHollyWoodScenario = true;
    }

    this.foodTensionHistory = [];
    this.timeTensionHistory = [];
    var historyLength = 50;
    var historySecCount = 0;
    this.currentHistoryIndex = 0;

    var _gameCanvas = [];
    this.init = function(canvas) {
        _gameCanvas = canvas;
        for (var i=0; i<historyLength; i++) {
            this.foodTensionHistory.push(0);
            this.timeTensionHistory.push(0);
        }
    };
    this.setScene= function(scene) { _scene = scene; };
    this.createScene = function() {
        var scene = [];
        scene.end = false;

        // create Stage
        var obstacleList = sceneDef.getObstacleList();
        scene.obstacleList = obstacleList;
        var backgroundList = sceneDef.getBackgroundList();
        scene.backgroundList = backgroundList;
        var stage = new sceneDef.Stage(Date.now(), 200000, 0);
        scene.Stages = [];
        scene.Stages.push(stage);
        scene.currentStage = 0;

        // create obstacles
        var spriteList = sceneDef.getCharacterSpriteList();
        for (var obj in spriteList) {
            var objName = obj.split("Sprites")[0];
            if (objName === "Player")
                scene["Player"] = new sceneDef.Player(0, "chenchen", 100, 120, "stillLeft");
            if (objName === "Student") {
                scene[objName] = [];
            }
            if (objName === "Professor") {
                scene[objName] = [];
            }
            if (objName === "Guard") {
                scene[objName] = [];
            }
        }


        // create characters
        return scene;
    };

    this.getCurrentWaveType = function() {
        if (_waveList[_waveIndex] === undefined)
            return "";
        return _waveList[_waveIndex].waveType;
    }
    this.determinePlayerPerformance = function() {
        var food,time;
        if (_scene.Player.foodCount / _scene.Player.foodMax < 0.3)
            food = 'LF';
        else
            food = 'HF';
        var currentStage = _scene.Stages[_scene.currentStage];

        var currentProgress = _scene.Player.x;
        var plannedProgress = (currentStage.timeMax-currentStage.timeLeft) / currentStage.timeMax * TILE_MAP.COLS * TILE_RESOLUTION;
        var diff = plannedProgress - currentProgress;
        if (diff < 0)
            time = 'HT';
        else {
            var ratio = diff / (currentStage.timeLeft / currentStage.timeMax * TILE_MAP.COLS * TILE_RESOLUTION);
            if (ratio > 0.05)
                time = 'LT';
            else
                time = 'HT';
        }


        var performance = food + time;
        var number;
        // the number of enemies generated
        switch(performance) {
            case "LFLT":
                number = 10;
                break;
            case "LFHT":
                number = 10;
                break;
            case "HFLT":
                number = 5;
                break;
            case "HFHT":
                number = 15;
                break;
            case "SPECIAL":
                number = 8;
            default :
                break;
        }
        return {number: number, type: performance};
    };

    this.specialSequence = function() {
        // determine how many enemies near the player
        if (_initHollyWoodScenario) {
            _initHollyWoodScenario = false;

            var player = _scene.Player;
            var frontCount = 0;

            // how many in front
            var xNearRatio = 0.05, xFarRatio = 0.2;
            var yRatio = 0.05;
            var xNearBoundary = player.x + _gameCanvas.width * xNearRatio;
            var xFarBoundary = player.x + _gameCanvas.width * xFarRatio;
            var yTopBoundary = player.y - _gameCanvas.height * yRatio;
            var yBottomBoundary = player.y + _gameCanvas.height * yRatio;
            for (var i=0; i < _scene.Student.length; i++) {
                var student = _scene.Student[i];
                if (student.x >= xNearBoundary && student.x <= xFarBoundary &&
                    student.y >= yTopBoundary  && student.y <= yBottomBoundary) {
                    // falls in front region
                    frontCount++;
                }
            }

            if (frontCount <= 300) {
                // not much enemies in fornt
                // first convert food left to time (in Sec)
                var timeLeft = player.foodCount / player.foodDecreaseSpeed * _inverseFPS;
                var timeMargin = 0.5;
                // timeLeft = timeToBeatOneEnemy + timeToWalkToThatEnemy + timeMargin
                // how long to beat one enemy (2sec)
                var timeToBeatOneEnemy = 3; //
                var timeToWalkToThatEnemy = timeLeft - timeToBeatOneEnemy - timeMargin;

                // convert walk time to walk distance, assume student per walk 10 frame, student per walk 8 frame
                var studentVelocity = STUDENT_VELOCITY * TILE_RESOLUTION / (10 * _inverseFPS);
                var playerVelocity = PLAYER_VELOCITY * TILE_RESOLUTION / (8 * _inverseFPS);
                // assume 80% chance player moves forward
                var walkDistance = timeToWalkToThatEnemy * (studentVelocity + playerVelocity * 0.2);
                if (walkDistance < 0) {
                    _inHollyWoodScenario = false;
                    return;
                }

                var studentX = walkDistance + player.x;
                var progressX = studentX;
                var studentY = player.y;
                // next wave check point
                var backgroundImage = graphicEngine.getBackgroundImage();
                var nextCheckPointX = (_waveIndex + 2) / 10 * backgroundImage.width;
                while(progressX < nextCheckPointX) {
                    var previousX = studentX;
                    var previousY = studentY;
                    while (TILE_MAP.GRIDS[Math.floor(studentY/TILE_RESOLUTION)][Math.floor(studentX/TILE_RESOLUTION)] === 1) {
                        if (studentX < 0 || studentY + TILE_RESOLUTION > _gameCanvas.height)
                            break;
                        studentX -= TILE_RESOLUTION;
                        studentY += TILE_RESOLUTION;
                    }

                    var newStudent = new sceneDef.Student(0, "student", studentX, studentY, "stillLeft", "aggressive", 0);
                    newStudent.foodAmount = 9;
                    _scene.Player.foodDecreaseSpeed = 100 * _inverseFPS / 70;
                    newStudent.radius = 50;
                    a_star([newStudent.tileX, newStudent.tileY], [player.tileX, player.tileY], TILE_MAP.GRIDS, TILE_MAP.COLS, TILE_MAP.ROWS, false);
                    _scene.Student.push(newStudent);

                    progressX += timeToBeatOneEnemy * studentVelocity;
                    studentX = previousX + walkDistance ;
                    studentY = previousY;
                }

            } else {
                _initHollyWoodScenario = false;
                _inHollyWoodScenario = false;
            }
        } else {
            var player = _scene.Player;
            var backgroundImage = graphicEngine.getBackgroundImage();

            if (player.x > (_waveIndex + 1) / 10 * backgroundImage.width) {
                //_waveIndex += 2;
                _scene.Player.foodDecreaseSpeed = 100 * _inverseFPS / 40;
                _inHollyWoodScenario = false;
            }
        }

    }

    // @author U095159L VARUN GANESH
    this.cornerSequence = function(){
        if (_initHollyWoodScenario) {
            _initHollyWoodScenario = false;
            //set starting points and spawn the crowders here
            var crowd_count = 8;
            var corner_index = 0;
            var coord_x = 0;
            var coord_y = 0;
            var divider_y = _gameCanvas.height/((crowd_count+2)/2);
            var divider_x = (_gameCanvas.width/2)/((crowd_count+2)/2);
            for (var i=0; i<crowd_count; i++) {
                if((_scene.Player.tileX * TILE_RESOLUTION) <= camera.posX){
                    if(i < crowd_count/2){
                        coord_x = 0;
                        coord_y = divider_y + (i*divider_y);
                    }
                    else{
                        coord_y = (TILE_MAP.ROWS - 1) * TILE_RESOLUTION;
                        coord_x = camera.posX - divider_x - ((i-(crowd_count/2))*divider_x);
                    }
                }
                else{
                    if(i < crowd_count/2){
                        coord_y = (TILE_MAP.ROWS - 1) * TILE_RESOLUTION;
                        coord_x = (camera.posX + camera.viewWidth/2) - divider_x - (i*divider_x);
                    }
                    else{
                        coord_y = divider_y + (i-(crowd_count/2))*divider_y;
                        coord_x = camera.posX + camera.viewWidth/2;
                    }

                }
                var newStudent = new sceneDef.Student(1, "student", coord_x, coord_y, "stillLeft", "corner", 1);
                newStudent.cornerIndex = corner_index;
                newStudent.center = [Math.floor(coord_x/TILE_RESOLUTION), Math.floor(coord_y/TILE_RESOLUTION)];
                newStudent.radius = 15;
                newStudent.path = a_star([newStudent.tileX, newStudent.tileY], newStudent.center,TILE_MAP.GRIDS, TILE_MAP.COLS, TILE_MAP.ROWS, false)

                _scene.Student.push(newStudent);

                if(corner_index < 7){
                    corner_index++;
                }
                else{
                    corner_index = 0;
                }
            }
        }
        else{
            //change cornering mode if not enough Students left
            var corner_count = 0;
            for(var i = 0; i<_scene.Student.length; i++){
                if(_scene.Student[i].AI_type == "corner"){
                    corner_count++;
                }
            }

            if(corner_count < 7){
                _inHollyWoodScenario = false;
                _waveIndex = _waveIndexTmp;
                for(var j = 0; j<_scene.Student.length; j++){
                    var student = _scene.Student[j];
                    if(student.AI_type == "corner"){
                        student.AI_type = "aggressive";
                    }
                }
                _inHollyWoodScenario = false;
            }
        }

    }

    this.updateHistory = function() {
        if (historySecCount < Math.floor(1/_inverseFPS)) {
            historySecCount++;
            return;
        } else {
            historySecCount = 0;

            var foodTension = _scene.Player.foodMax - _scene.Player.foodCount;

            var currentStage = _scene.Stages[_scene.currentStage];
            var currentProgress = _scene.Player.x;
            var plannedProgress = (currentStage.timeMax-currentStage.timeLeft) / currentStage.timeMax * TILE_MAP.COLS * TILE_RESOLUTION;
            var distanceLeft = currentStage.timeLeft / currentStage.timeMax * TILE_MAP.COLS * TILE_RESOLUTION;
            var timeTension;
            if (currentProgress > plannedProgress) {
                timeTension = 0;
            } else {
                timeTension= (plannedProgress - currentProgress) / distanceLeft;
            }

            this.foodTensionHistory[this.currentHistoryIndex] = foodTension;
            this.timeTensionHistory[this.currentHistoryIndex] = timeTension;
            this.currentHistoryIndex = (1+this.currentHistoryIndex) % historyLength;
        }
    }

    this.update = function() {
        this.updateHistory();
        if (_inHollyWoodScenario) {
            if (_hollyWoodScenarioIndex === 0) {
                //enter cornering scenario
                this.cornerSequence();
            } else {
                // enter hollywood scenario
                this.specialSequence();
            }

        } else {
            var ratio = _scene.Player.foodCount / _scene.Player.foodMax;
            if (ratio < 0.2) {
                _inHollyWoodScenario = true;
                _initHollyWoodScenario = true;
                if (_scene.Player.comboCount > 0.5 * _scene.Player.comboMax)
                    _hollyWoodScenarioIndex = 0;
                else
                    _hollyWoodScenarioIndex = 1;
            } else {
                ai.updateScene();
            }


        }
        ai.updateNPCs();
    }

    this.updateScene = function() {
        // first wave
        if (_waveList.length === 0) {
            // create the first wave of enemies
            var newWave = new EnemyWave(_waveIndex, 10, "LFLT", _scene.Player, _gameCanvas, _scene);
            _waveList.push(newWave);
        }

        // monitor the current wave state and introduce new enemy
        _waveList[_waveIndex].assignEnemy(_scene, _gameCanvas);

        var backgroundImage = graphicEngine.getBackgroundImage();
        var progress = Math.floor(_scene.Player.x / backgroundImage.width * 10);
        if (progress > _waveIndex) {
            // generate new wave
            _waveIndex++;
            var playerPerformance = this.determinePlayerPerformance();
            var newWave = new EnemyWave(_waveIndex, playerPerformance.number, playerPerformance.type, _scene.Player, _gameCanvas, _scene);
            _waveList.push(newWave);
        }
    };

    this.updateNPCs = function() {
        // Student AI
        this.updateStudents();
        this.updateProfessors();
        this.updateCampusSecurity();
    };

    this.updateStudents = function() {
        var students = _scene.Student;
        var player = _scene.Player;
        _hitTimer++;
        for (var i=0; i<students.length; i++) {
            // search whether player is with in attack range
            var student = students[i];
            var cameraLeftBoundary = camera.posX - camera.viewWidth / 2 > 0 ? camera.posX - camera.viewWidth /2 : 0;
            if (student.x < cameraLeftBoundary)
                continue;

            if (student.state.indexOf("hit") !== -1 || student.state.indexOf("death") !== -1) {
                return;
            }
            if (Math.abs(student.tileX-player.tileX) <= STUDENT_ATTACK_RANGE_X &&
                Math.abs(student.tileY-player.tileY) <= STUDENT_ATTACK_RANGE_Y) {
                // if within the range, choose between attacking or escaping
                switch (student.AI_type) {
                    case "corner":
                    case "aggressive" :
                        student.inRange = 1;
                        this.studentAttack(REACTION_AGGRESSIVE, student, player, student.fightSystemIndex);
                        break;
                    case "normal" :
                        student.inRange = 1;
                        this.studentAttack(REACTION_NORMAL, student, player, student.fightSystemIndex);
                        break;
                    case "passive" :
                        student.inRange = 1;
                        this.studentAttack(REACTION_PASSIVE, student, player, student.fightSystemIndex);
                        break;
                    default :
                        break;
                }
            } else {
                // if without the range, choose between approaching or escaping
                if (student.path.length == 0) {
                    if (student.tileX === undefined || student.tileY === undefined ||
                        student.destination[0] === undefined || student.destination[1] === undefined)
                        alert("undefined astar in init ai");
                    student.path = a_star([student.tileX, student.tileY], student.destination,
                        TILE_MAP.GRIDS, TILE_MAP.COLS, TILE_MAP.ROWS, false);
                    student.currentPathNodeIndex = 0;
                }
                switch (student.AI_type) {
                    case "corner" :
                        student.inRange = 0;
                        _specialGridList = [];
                        for (var x = player.tileX - 1; x <= player.tileX + 1; x++)
                        {
                            for (var y = player.tileY - 1; y <= player.tileY + 1; y++)
                            {
                                if(!(x == player.tileX && y == player.tileY)){
                                    _specialGridList.push( new SpecialWaveGrid(x, y, TILE_MAP.GRIDS[y][x] ));
                                }
                            }
                        }

                        if(_specialGridList[student.cornerIndex].occupied == 1){
                            while(_specialGridList[_specialGridIndex].occupied == 1){
                                if(_specialGridIndex < 7){
                                    _specialGridIndex++;
                                }
                                else{
                                    _specialGridIndex = 0;
                                }
                            }
                            student.destination = [_specialGridList[_specialGridIndex].x, _specialGridList[_specialGridIndex].y];
                        }
                        else{
                            student.destination = [_specialGridList[student.cornerIndex].x, _specialGridList[student.cornerIndex].y];
                        }

                        student.path = a_star([student.tileX, student.tileY], [student.destination[0],student.destination[1]],
                            TILE_MAP.GRIDS, TILE_MAP.COLS, TILE_MAP.ROWS, false);
                        student.currentPathNodeIndex = 0;


                        if(_specialGridIndex < 7){
                            _specialGridIndex++
                        }
                        else{
                            _specialGridIndex = 0;
                        }

                        break;

                    case "aggressive" :
                        // try to approach player
                        // recompute the path if player has changed position
                        student.inRange = 0;
                        if (Math.abs(player.tileX - student.center[0]) < student.radius &&
                            Math.abs(player.tileX - student.center[1]) < student.radius) {
                            if (student.destination[0] === player.tileX && student.destination[1] === player.tileY)
                                break;

                            student.destination = [player.tileX, player.tileY];
                            if (student.tileX === undefined || student.tileY === undefined ||
                                student.destination[0] === undefined || student.destination[1] === undefined)
                                alert("undefined astar in " + student.AI_type);

                            student.path = a_star([student.tileX, student.tileY], [student.destination[0],student.destination[1]],
                                TILE_MAP.GRIDS, TILE_MAP.COLS, TILE_MAP.ROWS, false);
                            student.currentPathNodeIndex = 0;
                        } else {
                            if (student.currentPathNodeIndex === student.path.length-1 && !student.watiforNextPath) {
                                student.watiforNextPath = true;
                                student.lastEndTime = Date.now();
                                //console.log("enter wait");
                            }
                            if (Date.now() - student.lastEndTime > 1500 && student.watiforNextPath) {
                                //console.log(Date.now() - student.lastEndTime);
                                student.watiforNextPath = false;
                                var radius = 5;
                                var newDestinationX = student.center[0] + Math.floor(radius * (Math.random()-0.5));
                                var newDestinationY = student.center[1] + Math.floor(radius * (Math.random()-0.5));
                                while (newDestinationX < 0 || newDestinationX > TILE_MAP.COLS -1 ||
                                    newDestinationY < 0 || newDestinationY > TILE_MAP.ROWS -1) {
                                    newDestinationX = student.center[0] + Math.floor(radius * (Math.random()-0.5));
                                    newDestinationY = student.center[1] + Math.floor(radius * (Math.random()-0.5));
                                }
                                student.destination = [newDestinationX,
                                    newDestinationY];

                                if (student.tileX === undefined || student.tileY === undefined ||
                                    student.destination[0] === undefined || student.destination[1] === undefined)
                                    alert("undefined astar in " + student.AI_type);
                                var aStart = Date.now();
                                student.path = a_star([student.tileX, student.tileY], student.destination,
                                    TILE_MAP.GRIDS, TILE_MAP.COLS, TILE_MAP.ROWS, false);
                                console.log(Date.now()-aStart);
                                student.currentPathNodeIndex = 0;
                            }

                        }
                        break;
                    case "normal" :
                        // just wander around
                        // compute a new random position
                        student.inRange = 0;
                        if (student.currentPathNodeIndex == student.path.length-1) {
                            var randomX = Math.floor(Math.random() * 4);
                            var randomY = Math.floor(Math.random() * 4);
                            student.destination = [randomX, randomY];
                            if (student.tileX === undefined || student.tileY === undefined ||
                                student.destination[0] === undefined || student.destination[1] === undefined)
                                alert("undefined astar in " + student.AI_type);

                            student.path = a_star([student.tileX, student.tileY], student.destination,
                                TILE_MAP.GRIDS, TILE_MAP.COLS, TILE_MAP.ROWS, false);
                            student.currentPathNodeIndex = 0;
                        }
                        break;
                    case "passive" :
                        // always try to escape from player
                        student.inRange = 0;
                        if (student.accumEscapeTime < student.EscapeTimeMax) {
                            if (Math.abs(student.tileX-player.tileX) < ESCAPE_THRESHOLD && Math.abs(student.tileY-player.tileY) < ESCAPE_THRESHOLD) {
                                if (!student.inEscape) {
                                    student.currentEscapeStart = Date.now();
                                    student.inEscape = true;
                                }
                                if (student.inEscape) {
                                    student.accumEscapeTime += (Date.now() - student.currentEscapeStart);
                                    student.currentEscapeStart = Date.now();
                                }

                                var dx = student.tileX - player.tileX;
                                var dy = student.tileY - player.tileY;
                                if (student.inEscape && student.tileX === student.destination[0] && student.tileY === student.destination[1]) {
                                    var destination = [student.tileX , student.tileY];
                                    var toBreak = false;
                                    while (Math.abs(destination[0] - player.tileX) < ESCAPE_THRESHOLD &&
                                        Math.abs(destination[1] -  player.tileY) < ESCAPE_THRESHOLD
                                        ) {

                                        destination[0] += dx;
                                        destination[1] += dy;
                                        if (destination[0] >= TILE_MAP.COLS-1 || destination[0] <= 0) {
                                            destination[0] = TILE_MAP.COLS-1;
                                            toBreak = true;
                                        }
                                        if (destination[0] <= 0) {
                                            destination[0] = 0;
                                            toBreak = true;
                                        }
                                        if (destination[1] >= TILE_MAP.ROWS-1) {
                                            destination[1] = TILE_MAP.ROWS-1;
                                            toBreak = true;
                                        }
                                        if (destination[1] <=0) {
                                            destination[1] = 0;
                                            toBreak = true;
                                        }
                                        if (toBreak)
                                            break;

                                    }
                                    student.destination = destination;
                                    if (student.tileX === undefined || student.tileY === undefined ||
                                        student.destination[0] === undefined || student.destination[1] === undefined)
                                        alert("undefined astar in " + student.AI_type);

                                    student.path = a_star([student.tileX, student.tileY], student.destination,
                                        TILE_MAP.GRIDS, TILE_MAP.COLS, TILE_MAP.ROWS, false);
                                    student.currentPathNodeIndex = 0;
                                }

                            } else {
                                student.inEscape = false;
                            }
                        } else {
                            student.destination = [player.tileX, player.tileY];
                            if (student.tileX === undefined || student.tileY === undefined ||
                                student.destination[0] === undefined || student.destination[1] === undefined)
                                alert("undefined astar in " + student.AI_type);

                            student.path = a_star([student.tileX, student.tileY], [student.destination[0]+1,student.destination[1]],
                                TILE_MAP.GRIDS, TILE_MAP.COLS, TILE_MAP.ROWS, false);
                            student.currentPathNodeIndex = 0;
                        }
                        break;
                    default:
                        break;
                }
                if (student.path.length == 0) {
                    break;
                }
                // move to the next node on the path
                if (student.tileX == student.path[student.currentPathNodeIndex].x &&
                    student.tileY == student.path[student.currentPathNodeIndex].y ) {
                    if (student.currentPathNodeIndex < student.path.length-1) {
                        student.currentPathNodeIndex++;
                    }
                }

                if (student.state.indexOf("hit") === -1 && student.state.indexOf("death") === -1) {
                    if ( student.path[student.currentPathNodeIndex].x - student.tileX > 0)
                        student.updateState("walkRight");
                    if ( student.path[student.currentPathNodeIndex].x - student.tileX < 0)
                        student.updateState("walkLeft");
                    if ( student.path[student.currentPathNodeIndex].y - student.tileY > 0)
                        student.updateState("walkDownRight");
                    if ( student.path[student.currentPathNodeIndex].y - student.tileY < 0)
                        student.updateState("walkUpRight");
                }

                FIGHT_ACTIVE = 0;
                for (var j=0; j<students.length; j++) {
                    var student = students[j];
                    if(student.inRange == 1){
                        FIGHT_ACTIVE = 1;
                    }
                }
            }
        }
    };

    this.updateProfessors = function() {
        var profs = _scene.Professor;
        var player = _scene.Player;

        for (var i=0; i<profs.length; i++) {
            // search whether player is with in attack range
            var prof = profs[i];

            if (prof.state.indexOf("hit") !== -1 || prof.state.indexOf("death") !== -1) {
                return;
            }

            if (prof.currentPathNodeIndex < prof.path.length -1) {
                // move to the next node on the path
                if (prof.tileX == prof.path[prof.currentPathNodeIndex].x &&
                    prof.tileY == prof.path[prof.currentPathNodeIndex].y ) {
                    if (prof.currentPathNodeIndex < prof.path.length-1) {
                        prof.currentPathNodeIndex++;
                    }
                }

                if (prof.state.indexOf("hit") === -1 && prof.state.indexOf("death") === -1) {
                    if ( prof.path[prof.currentPathNodeIndex].x - prof.tileX > 0)
                        prof.updateState("walkRight");
                    if ( prof.path[prof.currentPathNodeIndex].x - prof.tileX < 0)
                        prof.updateState("walkLeft");
                    if ( prof.path[prof.currentPathNodeIndex].y - prof.tileY > 0)
                        prof.updateState("walkDownRight");
                    if ( prof.path[prof.currentPathNodeIndex].y - prof.tileY < 0)
                        prof.updateState("walkUpRight");
                }
                return;
            }
            prof.state = "talkRight";
            if (prof.state.indexOf("talk") !== -1 ) {

                for (var i=0; i < _scene.Student.length; i++) {
                    var student = _scene.Student[i];
                    if (Math.abs(prof.tileX - student.tileX) < prof.effectRadius &&
                        Math.abs(prof.tileY - student.tileY) < prof.effectRadius )
                        student.slowedDown = prof.studentSlowedDown;
                    else
                        student.slowedDown = 1;
                }
                var player = _scene.Player;
                if (Math.abs(prof.tileX - player.tileX) < prof.effectRadius &&
                    Math.abs(prof.tileY - player.tileY) < prof.effectRadius )
                    player.slowedDown = prof.playerSlowedDown;
                else
                    player.slowedDown = 1;
            }
        }
    };

    this.updateCampusSecurity = function() {
        var guards = _scene.Guard;
        var player = _scene.Player;

        for (var i=0; i<guards.length; i++) {
            // search whether player is with in attack range
            var guard = guards[i];

            if (guard.state.indexOf("hit") !== -1 || guard.state.indexOf("death") !== -1) {
                return;
            }
            if (guard.currentPathNodeIndex === guard.path.length -1) {
                guard.path = a_star([guard.tileX,  guard.tileY], [player.tileX + 1,player.tileY],
                    TILE_MAP.GRIDS, TILE_MAP.COLS, TILE_MAP.ROWS, false);
                guard.currentPathNodeIndex = 0;
            }
            if (guard.currentPathNodeIndex < guard.path.length -1) {
                // move to the next node on the path
                if (guard.tileX == guard.path[guard.currentPathNodeIndex].x &&
                    guard.tileY == guard.path[guard.currentPathNodeIndex].y ) {
                    if (guard.currentPathNodeIndex < guard.path.length-1) {
                        guard.currentPathNodeIndex++;
                    }
                }

                if (guard.state.indexOf("hit") === -1 && guard.state.indexOf("death") === -1) {
                    if ( guard.path[guard.currentPathNodeIndex].x - guard.tileX > 0)
                        guard.updateState("runRight");
                    if ( guard.path[guard.currentPathNodeIndex].x - guard.tileX < 0)
                        guard.updateState("runLeft");
                    if ( guard.path[guard.currentPathNodeIndex].y - guard.tileY > 0)
                        guard.updateState("runDownRight");
                    if ( guard.path[guard.currentPathNodeIndex].y - guard.tileY < 0)
                        guard.updateState("runUpRight");
                }
                return;
            }

        }
    };

    // @author U095159L VARUN GANESH
    this.studentAttack = function( response, student, player, fightsystem_index ){
        if(fightsystem_index == 0){
            _NPCmove = normalFightSystem.chooseMove();
        }

        else if(fightsystem_index == 1){
            _NPCmove = hardFightSystem.chooseMove();
        }

        if(parseInt(_hitTimer)%response == 0){
            switch(_NPCmove){
                case PUNCH_CODE:
                    var move = Math.floor(Math.random()*3);
                    switch(move){
                        case 0:
                            if(player.x < student.x){
                                student.updateState("punchLeft");
                            }
                            else{
                                student.updateState("punchRight");
                            }
                            break;
                        case 1:
                            if(player.x < student.x){
                                student.updateState("kickMidLeft");
                            }
                            else{
                                student.updateState("kickMidRight");
                            }
                            break;
                        case 2:
                            if(player.x < student.x){
                                student.updateState("kickHighLeft");
                            }
                            else{
                                student.updateState("kickHighRight");
                            }
                            break;
                    }
                    break;
                case KICK_CODE:
                    if(player.x < student.x){
                        student.updateState("kickLowLeft");
                    }
                    else{
                        student.updateState("kickLowRight");
                    }
                    break;
                case BLOCK_CODE:
                    //substitute for block
                    if(player.x < student.x){
                        student.updateState("blockLeft");
                    }
                    else{
                        student.updateState("blockRight");
                    }
                    break;
                case JUMP_CODE:
                    if(player.x < student.x){
                        student.updateState("jumpLeft");
                    }
                    else{
                        student.updateState("jumpRight");
                    }
                    break;
                default:
                    break;
            }
        }
        // console.log(student.state);
    }
}
var ai = new AI();

// @author U095159L VARUN GANESH
var SpecialWaveGrid = function ( tile_x, tile_y, occupied){
    this.x = tile_x;
    this.y = tile_y;
    this.occupied = occupied;
}

// @author U099151W Sun Chenchen
// all functions in this file  are implemented by the author unless specifically indicated
var EnemyWave = function(index, totalNumber, manipulationType, player, canvas, scene) {
    this.index = index;
    this.totalNumber = totalNumber;
    this.numberLeft = totalNumber;
    this.enemies = [];
    this.lastSubWaveTime = Date.now()-5000;
    this.subWaveDuration = 5000;
    this.waveType = manipulationType;

    function generateNewEnemyPosition(ratio, player, canvas) {
        var canvasTileWidth = canvas.width / TILE_RESOLUTION;
        var spreadRadius = Math.floor(canvasTileWidth * ratio);
        var randAngle, tileX, tileY;

        randAngle = Math.PI * (Math.random() - 0.5);
        tileX = player.tileX + Math.floor(spreadRadius * Math.cos(randAngle));
        tileY = Math.floor(TILE_MAP.ROWS / 2)  + Math.floor(spreadRadius * Math.sin(randAngle));
        while ( (tileY < (TILE_MAP.ROWS -1) * 0.3 || tileY > (TILE_MAP.ROWS -1) * 0.7)
            || TILE_MAP.GRIDS[tileY][tileX] === 1) {
            randAngle = Math.PI * (Math.random() - 0.5);
            tileX = player.tileX + Math.floor(spreadRadius * Math.cos(randAngle));
            tileY = Math.floor(TILE_MAP.ROWS / 2)  + Math.floor(spreadRadius * Math.sin(randAngle));
        }

        var x,y;
        var newStudent;

        if (tileY > (TILE_MAP.ROWS - 1)/2)
            side = 1;
        else
            side = 0;
        if (tileX < (camera.posX + camera.viewWidth/2) / TILE_RESOLUTION &&
            tileX + canvas.width * 0.2/ TILE_RESOLUTION > (camera.posX + camera.viewWidth/2) / TILE_RESOLUTION )
            side = 2;

        if (side === 0) {
            x = tileX * TILE_RESOLUTION;
            y = 0;
        } else if (side === 1) {
            x = tileX * TILE_RESOLUTION;
            y = (TILE_MAP.ROWS - 1) * TILE_RESOLUTION;

        } else {
            var studentTileX = (tileX + canvas.width * 0.2/ TILE_RESOLUTION);
            var studentTileY = tileY;
            while (TILE_MAP.GRIDS[studentTileY][studentTileX] === 1)
                studentTileX++;
            x = studentTileX * TILE_RESOLUTION;
            y = studentTileY * TILE_RESOLUTION;
        }
        return {studentX : x, studentY : y, tileX : tileX, tileY : tileY};
    }

    // enemy initialization
    this.studentList = [];
    this.waveType = manipulationType;
    switch(manipulationType) {
        // low food low time
        case "LFLT":

            for (var i=0; i<totalNumber; i++) {
                var ratio = 0.4; // hard-coded
                var position = generateNewEnemyPosition(ratio, player, canvas);
                var newStudent = new sceneDef.Student(1, "student", position.studentX, position.studentY, "stillLeft", "aggressive", 0);
                newStudent.center = [position.tileX, position.tileY];
                // closer search range
                newStudent.radius = 15;
                if (newStudent.tileX === undefined || newStudent.tileY === undefined ||
                    newStudent.center[0] === undefined || newStudent.center[1] === undefined)
                    alert("undefined astar in " + manipulationType);
                newStudent.path = a_star([newStudent.tileX, newStudent.tileY], newStudent.center,TILE_MAP.GRIDS, TILE_MAP.COLS, TILE_MAP.ROWS, false)

                this.enemies.push(newStudent);
            }
            break;
        // low food high time
        case "LFHT":
            for (var i=0; i<totalNumber; i++) {
                var ratio = 0.5; // hard-coded
                var position = generateNewEnemyPosition(ratio, player, canvas);
                var newStudent =  new sceneDef.Student(1, "student", position.studentX, position.studentY, "stillLeft", "aggressive", 1);
                newStudent.center = [position.tileX, position.tileY];
                newStudent.radius = 15;
                if (newStudent.tileX === undefined || newStudent.tileY === undefined ||
                    newStudent.center[0] === undefined || newStudent.center[1] === undefined)
                    alert("undefined astar in " + manipulationType);
                newStudent.path = a_star([newStudent.tileX, newStudent.tileY], newStudent.center,TILE_MAP.GRIDS, TILE_MAP.COLS, TILE_MAP.ROWS, false)

                this.enemies.push(newStudent);
            }
            var tileX = player.tileX + 20;
            var tileY = player.tileY;
            while(TILE_MAP.GRIDS[tileY][tileX] === 1) {
                tileX += 1;
            }
            scene.Guard.push( new sceneDef.Guard(0,'Guard',tileX * TILE_RESOLUTION, tileY * TILE_RESOLUTION, "stillLeft") );
            scene.Guard[0].path = a_star([scene.Guard[0].tileX,  scene.Guard[0].tileY], [scene.Player.tileX + 3,scene.Player.tileY],
                TILE_MAP.GRIDS, TILE_MAP.COLS, TILE_MAP.ROWS, false);
            break;
        // high food low time
        case "HFLT":
            for (var i=0; i<totalNumber; i++) {
                var ratio = 0.6; // hard-coded
                var position = generateNewEnemyPosition(ratio, player, canvas);
                var newStudent = new sceneDef.Student(1, "student", position.studentX, position.studentY, "stillLeft", "passive", 1);

                if (newStudent.tileX === undefined || newStudent.tileY === undefined ||
                    newStudent.destination[0] === undefined || newStudent.destination[1] === undefined)
                    alert("undefined astar in " + manipulationType);
                newStudent.destination = [position.tileX, position.tileY];
                newStudent.path = a_star([newStudent.tileX, newStudent.tileY], newStudent.destination,TILE_MAP.GRIDS, TILE_MAP.COLS, TILE_MAP.ROWS, false)

                this.enemies.push(newStudent);

            }

            break;
        // high food high time
        case "HFHT":
            for (var i=0; i<totalNumber; i++) {
                var ratio = 0.8; // hard-coded
                var position = generateNewEnemyPosition(ratio, player, canvas);
                var newStudent = new sceneDef.Student(1, "student", position.studentX, position.studentY, "stillLeft", "passive", 1);

                newStudent.destination = [position.tileX, position.tileY];
                if (newStudent.tileX === undefined || newStudent.tileY === undefined ||
                    newStudent.destination[0] === undefined || newStudent.destination[1] === undefined)
                    alert("undefined astar in " + manipulationType);
                newStudent.path = a_star([newStudent.tileX, newStudent.tileY], newStudent.destination,TILE_MAP.GRIDS, TILE_MAP.COLS, TILE_MAP.ROWS, false)

                this.enemies.push(newStudent);
            }
            var tileX = player.tileX + 20;
            var tileY = player.tileY;
            while(TILE_MAP.GRIDS[tileY][tileX] === 1) {
                tileX += 1;
            }
            scene.Guard.push( new sceneDef.Guard(0,'Guard',tileX * TILE_RESOLUTION, tileY * TILE_RESOLUTION, "stillLeft") );
            scene.Guard[0].path = a_star([scene.Guard[0].tileX,  scene.Guard[0].tileY], [scene.Player.tileX + 3,scene.Player.tileY],
                TILE_MAP.GRIDS, TILE_MAP.COLS, TILE_MAP.ROWS, false);
            break;

        default:
            break;
    }
    // introduce random profs
    var isProf = Math.floor(Math.random() * 2);
    if (isProf) {
        var tileX = player.tileX + 20;
        var tileY = player.tileY;
        while(TILE_MAP.GRIDS[tileY][tileX] === 1) {
            tileX += 1;
        }
        scene.Professor.push( new sceneDef.Professor(0,'Professor',tileX * TILE_RESOLUTION, tileY * TILE_RESOLUTION, "stillLeft") );
        scene.Professor[0].path = a_star([scene.Professor[0].tileX,  scene.Professor[0].tileY], [scene.Professor[0].tileX,  scene.Professor[0].tileY],
            TILE_MAP.GRIDS, TILE_MAP.COLS, TILE_MAP.ROWS, false);
    }

    // introduce random cornering scenario
    var isCorner = Math.floor(Math.random() * 10);
    if (isCorner < 1) {
        ai.setInHolly();
        ai.cornerSequence();
        ai.clearInHolly();
    }

    this.assignEnemy = function(scene, canvas) {
        var timeElapsed = Date.now() - this.lastSubWaveTime;
        if (timeElapsed > this.subWaveDuration) {

//            scene.Professor.push( new sceneDef.Professor(0,'Professor',200,200, "stillLeft") );
//            scene.Professor[0].path = a_star([scene.Professor[0].tileX,  scene.Professor[0].tileY], [2,2],
//                TILE_MAP.GRIDS, TILE_MAP.COLS, TILE_MAP.ROWS, false);

           /* scene.Guard.push( new sceneDef.Guard(0,'Guard',200,200, "stillLeft") );
            scene.Guard[0].path = a_star([scene.Guard[0].tileX,  scene.Guard[0].tileY], [scene.Player.tileX +1,scene.Player.tileY],
               TILE_MAP.GRIDS, TILE_MAP.COLS, TILE_MAP.ROWS, false);*/

            this.lastSubWaveTime = Date.now();
            // assign new enemies
            var number = Math.floor(Math.random() * 3) + 2;
            if (number > this.numberLeft)
                number = this.numberLeft;
            for (var i=0; i<number; i++) {
                var index = this.totalNumber - this.numberLeft;
                scene.Student.push(this.enemies[index]);
                this.numberLeft--;
            }
        }

    }
}